﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EntityDataModel;

namespace DataAccessLayer
{
    public class MessageDAL
    {
        /// <summary>     
        /// Get all messages where the username in parameter is the receiver and the message visibility is public.
        /// It also returns all status messages for the given username.
        /// </summary>
        public static List<Message> GetAllPublicMessagesForUser(String userName)
        {
            try
            {
                using (liveCPEEntities context = new liveCPEEntities())
                {
                    IOrderedQueryable<Message> messages = from m in context.Messages where m.ReceiverName == userName & m.IsPrivate == false orderby m.PublicationDate descending select m;

                    foreach (Message m in messages)
                    {
                        m.PublicationAge = Math.Ceiling((DateTime.Now - m.PublicationDate.Value).TotalSeconds).ToString();
                    }
                    return messages.ToList();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                throw new Exception(sf.GetMethod().Name + " unexpected error", e);
            }
        }

        /// <summary>     
        /// Get all messages where the username in parameter is the receiver and the message visibility is private.
        /// It also returns all status messages for the given username.
        /// </summary>
        public static List<Message> GetAllPrivateMessagesForUser(String userName)
        {
            try
            {
                using (liveCPEEntities context = new liveCPEEntities())
                {
                    IOrderedQueryable<Message> messages = from m in context.Messages where m.ReceiverName == userName & m.IsPrivate == true orderby m.PublicationDate descending select m;

                    foreach (Message m in messages)
                    {
                        m.PublicationAge = Math.Ceiling((DateTime.Now - m.PublicationDate.Value).TotalSeconds).ToString();
                    }
                    return messages.ToList();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                throw new Exception(sf.GetMethod().Name + " unexpected error", e);
            }
        }

        /// <summary>     
        /// Get all messages where the username or his friends are the receiver.
        /// It also returns all status messages.
        /// </summary>
        public static List<Message> GetAllPublicMessages(String userName)
        {
            try
            {
                using (liveCPEEntities context = new liveCPEEntities())
                {
                    // Get friends usernames
                    IQueryable<String> friends1 = from f in context.Friendships where f.User1Name == userName & f.IsActive == true select f.User2Name;
                    IQueryable<String> friends2 = from f in context.Friendships where f.User2Name == userName & f.IsActive == true select f.User1Name;
                    List<String> friends = (friends1.Union(friends2)).ToList();
                    // Add current username
                    friends.Add(userName);

                    // Get all messages for the user and his friends
                    List<Message> messages = new List<Message>();
                    foreach(String friendName in friends) 
                    {
                        messages = messages.Union(from m in context.Messages where m.ReceiverName == friendName & m.IsPrivate == false select m).ToList();
                    }
                    
                    // Add the publication date
                    foreach (Message m in messages)
                    {
                        m.PublicationAge = Math.Ceiling((DateTime.Now - m.PublicationDate.Value).TotalSeconds).ToString();
                    }

                    return messages.OrderByDescending(m => m.PublicationDate).ToList();
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                throw new Exception(sf.GetMethod().Name + " unexpected error", e);
            }
        }

        /// <summary>     
        /// Create a public message between the sender and receiver users in parameter
        /// </summary>
        public static void SendPublicMessage(String senderName, String receiverName, String messageContent)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid messageId = System.Guid.NewGuid();
                    //User user = context.Users.First(u => u.UserName == senderName);
                    
                    context.Messages.AddObject(new Message
                    {
                        MessageID = messageId,
                        SenderName = senderName,
                        ReceiverName = receiverName,
                        NumLike = 0,
                        PublicationDate = DateTime.Now,
                        IsPrivate = false,
                        IsVisible = true,
                        MessageContent = messageContent
                    });

                    Guid notificationId = System.Guid.NewGuid();
                    context.Notifications.AddObject(new Notification
                    {
                        NotificationID = notificationId,
                        UserName = receiverName,
                        MessageID = messageId,
                        PublicationDate = DateTime.Now
                    });

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>     
        /// Create a private message between the sender and receiver users in parameter
        /// </summary>
        public static void SendPrivateMessage(String senderName, String receiverName, String messageContent)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid messageId = System.Guid.NewGuid();
                    //User user = context.Users.First(u => u.UserName == senderName);
                    
                    context.Messages.AddObject(new Message
                    {
                        MessageID = messageId,
                        SenderName = senderName,
                        ReceiverName = receiverName,
                        NumLike = 0,
                        PublicationDate = DateTime.Now,
                        IsPrivate = true,
                        IsVisible = true,
                        MessageContent = messageContent
                    });

                    Guid notificationId = System.Guid.NewGuid();
                    context.Notifications.AddObject(new Notification
                    {
                        NotificationID = notificationId,
                        UserName = receiverName,
                        MessageID = messageId,
                        PublicationDate = DateTime.Now
                    });

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>     
        /// Create a private message between the sender and receiver users in parameter
        /// </summary>
        public static void AddComment(String senderName, Message message, String messageContent)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid commentId = System.Guid.NewGuid();
                    //User user = context.Users.First(u => u.UserName == senderName);

                    context.Comments.AddObject(new Comment
                    {
                        CommentID = commentId,
                        MessageID = message.MessageID,
                        AuthorName = senderName,
                        NumLike = 0,
                        PublicationDate = DateTime.Now,
                        IsVisible = true,
                        CommentContent = messageContent
                    });

                    // Add a notification to original message sender
                    Guid notification1Id = System.Guid.NewGuid();
                    context.Notifications.AddObject(new Notification
                    {
                        NotificationID = notification1Id,
                        UserName = message.SenderName,
                        MessageID = message.MessageID,
                        PublicationDate = DateTime.Now
                    });

                    // Add a notification to original message receiver
                    Guid notification2Id = System.Guid.NewGuid();
                    context.Notifications.AddObject(new Notification
                    {
                        NotificationID = notification2Id,
                        UserName = message.ReceiverName,
                        MessageID = message.MessageID,
                        PublicationDate = DateTime.Now
                    });

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Get Comment by commentId
        /// </summary>
        public static Comment GetCommentById(String commentId)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid guid = new Guid(commentId);
                    return context.Comments.First(c => c.CommentID == guid);
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Get Message by messageId
        /// </summary>
        public static Message GetMessageById(String messageId)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid guid = new Guid(messageId);
                    return context.Messages.First(m => m.MessageID == guid);
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Add a like to the given comment
        /// </summary>
        public static void LikeComment(User user, Comment comment)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Comment cmt = context.Comments.First(c => c.CommentID == comment.CommentID);
                    cmt.NumLike += 1;

                    Message msg = context.Messages.First(m => m.MessageID == comment.MessageID);

                    Guid likeId = System.Guid.NewGuid();
                    Guid notificationId = System.Guid.NewGuid();

                    context.Likes.AddObject(new Like
                    {
                        LikeID = likeId,
                        CommentID = cmt.CommentID,
                        AuthorName = user.UserName,
                        PublicationDate = DateTime.Now,
                    });

                    if (cmt.AuthorName != user.UserName)
                    {
                        context.Notifications.AddObject(new Notification
                        {
                            NotificationID = notificationId,
                            LikeID = likeId,
                            UserName = msg.SenderName,
                            MessageID = msg.MessageID,
                            PublicationDate = DateTime.Now
                        });
                        if (msg.SenderName != msg.ReceiverName)
                        {
                            context.Notifications.AddObject(new Notification
                            {
                                NotificationID = notificationId,
                                LikeID = likeId,
                                UserName = msg.ReceiverName,
                                MessageID = msg.MessageID,
                                PublicationDate = DateTime.Now
                            });
                        }
                    }

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Add a like to the given message
        /// </summary>
        public static void LikeMessage(User user, Message message)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Message msg = context.Messages.First(m => m.MessageID == message.MessageID);
                    msg.NumLike += 1;

                    Guid likeId = System.Guid.NewGuid();
                    Guid notificationId = System.Guid.NewGuid();

                    context.Likes.AddObject(new Like
                    {
                        LikeID = likeId,
                        MessageID = msg.MessageID,
                        AuthorName = user.UserName,
                        PublicationDate = DateTime.Now,
                    });

                    if (msg.SenderName != user.UserName)
                    {
                        context.Notifications.AddObject(new Notification
                        {
                            NotificationID = notificationId,
                            LikeID = likeId,
                            UserName = msg.SenderName,
                            MessageID = msg.MessageID,
                            PublicationDate = DateTime.Now
                        });
                    }

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Check if the user already likes the given comment
        /// </summary>
        public static Boolean IsUserAlreadyLikingComment(User user, Comment comment)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    IQueryable<Like> like = from l in context.Likes where l.CommentID == comment.CommentID & l.AuthorName == user.UserName select l;
                    if (like == null || like.ToList().Count == 0)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Check if the user already likes the given message
        /// </summary>
        public static Boolean IsUserAlreadyLikingMessage(User user, Message message)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    IQueryable<Like> like = from l in context.Likes where l.MessageID == message.MessageID & l.AuthorName == user.UserName select l;
                    if (like == null || like.ToList().Count == 0)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }

        /// <summary>
        /// Get all comments for the given message
        /// </summary>
        public static List<Comment> GetAllCommentsForMessage(string messageID)
        {
            using (liveCPEEntities context = new liveCPEEntities())
            {
                try
                {
                    Guid msgId = new Guid(messageID);
                    IQueryable<Comment> comments = from c in context.Comments where c.MessageID == msgId orderby c.PublicationDate ascending select c;
                    foreach (Comment c in comments)
                    {
                        c.PublicationAge = Math.Ceiling((DateTime.Now - c.PublicationDate).TotalSeconds).ToString();
                    }
                    return comments.ToList();
                }
                catch (Exception e)
                {
                    System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame();
                    LogDAL.AddLog(e.Message, "Error", DataAccessLayerSettings.Default.ApplicationName, sf.GetMethod().Name, DateTime.Now);
                    throw new Exception(sf.GetMethod().Name + " unexpected error", e);
                }
            }
        }
    }
}
